﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.Linq;

namespace TRAC_Demo
{
    // T.R.A.C.
    // Main Author: Ryan Redburn
    // Date: 9/30/2013

    #region [ Updates ]

    // Updated: 10/3/2013
    // By: Ryan Redburn
    // Updates: Changed the CreateXDoc and CreateUser methods to accept and store color,
    // created a method, RetrieveUserColor, to return user color from the xml doc

    // Updated: 10/4/2013
    // By: Ryan Redburn
    // Updates: Changed the GetUserInfo method name to GetAcctInfo, the method purpose has been changed
    // to return all account info from the specified user

    // Updated: 10/4/2013
    // By: Ryan Redburn
    // Updates: Added a method for changing user color and added an empID attribute to the xml doc

    // Updated: 10/31/2013
    // By: Ryan Redburn
    // Updates: Major overhaul, changed numerous methods to use Lambda functions when only looking for one result,
    // removed and replaced methods required to create or load the xml document, cleaned up messy code, there are now
    // two different methods used to add accounts based on account type, the backup process is now handled differently

    // Updated: 12/2/2013
    // By: Ryan Redburn
    // Updates: Added a method to check for user accounts before executing actions

    #endregion

    static class AccountKeeper
    {
        #region [ Fields ]

        private static XDocument Accounts;

        #endregion

        #region [ Methods ]

        // Determines the action required to get the class the necessary xml document
        // Returns true on load, false on create
        public static bool Intialize()
        {
            if (File.Exists("accts.xml"))
            {
                LoadXDoc(Path.GetFullPath("accts.xml"));
                return false;
            }
            else
            {
                if (File.Exists("accts_backup.xml"))
                {
                    LoadXDoc(Path.GetFullPath("accts_backup.xml"));
                    return false;
                }
                else
                {
                    CreateXDoc();
                    return true;
                }
            }
        }

        // Creates a backup for the xml document
        public static void BackUp()
        {
            Accounts.Save("accts_backup.xml");
        }

        // Adds a standard account to the xml document
        public static void CreateStandardAcct(string userID, string empID, string password, string color)
        {
            // Looks for accounts with the given employee id
            // This is checked in order to prevent duplicate accounts
            var empCheck = from children in Accounts.Element("LocalAccount").Element("Users").Descendants("User")
                           where children.Attribute("empID").Value == empID
                           select children;

            if (empCheck.Count() == 0)
            {
                Accounts.Element("LocalAccount").Element("Users").Add(new XElement("User", userID,
                new XAttribute("empID", empID),
                new XAttribute("type", "Standard"),
                new XAttribute("password", password),
                new XAttribute("color", color)));
                Accounts.Save("accts.xml");
            }
            else
            {
                throw new XmlException("An account already exists for this user.");
            }
        }

        // Adds an administrator account to the xml document
        public static void CreateAdminAcct(string userID, string empID, string password, string color)
        {
            // Looks for accounts with the given employee id
            // This is checked in order to prevent duplicate accounts
            var empCheck = from children in Accounts.Element("LocalAccount").Element("Users").Descendants("User")
                           where children.Attribute("empID").Value == empID
                           select children;

            if (empCheck.Count() == 0)
            {
                Accounts.Element("LocalAccount").Element("Users").Add(new XElement("User", userID,
                new XAttribute("empID", empID),
                new XAttribute("type", "Administrator"),
                new XAttribute("password", password),
                new XAttribute("color", color)));
                Accounts.Save("accts.xml");
            }
            else
            {
                throw new XmlException("An account already exists for this user.");
            }
        }

        // Validates the user log in credentials
        public static bool ValidateUser(string userID, string password)
        {
            bool logInValidate = false;

            // Lambda function that finds the user acconut that is attempting to log in
            XElement user =
                Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();

            // Confirms the password given is correct for this account
            if (user.Attribute("password").Value == password)
            {
                logInValidate = true;
            }

            return logInValidate;
        }

        // Validates the admin code for account creation
        public static bool ValidateAdmin(string adminCode)
        {
            bool adminValidate = false;

            // Compares the given admin code with the admin code in the xml doc
            if (adminCode == (string)Accounts.Element("LocalAccount").Element("Code").Element("AdminCode"))
            {
                adminValidate = true;
            }

            return adminValidate;
        }

        // Returns the color the user specfied at account creation
        public static string RetrieveUserColor(string userID)
        {
            string color = "";

            // Lambda function that finds the user using their user ID
            XElement user =
                Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();

            color = user.Attribute("color").Value;

            return color;
        }

        // Returns a collection of user information based on userID
        public static List<string> GetAcctInfo(string userID)
        {
            List<string> userInfo = new List<string>();

            XElement acct =
                Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();

            // Adds all the specified account info to the returned list
            userInfo.Add(acct.Value);
            userInfo.Add(acct.Attribute("empID").Value);
            userInfo.Add(acct.Attribute("password").Value);
            userInfo.Add(acct.Attribute("type").Value);
            userInfo.Add(acct.Attribute("color").Value);

            return userInfo;
        }

        // Returns a collection of user information based on empID
        public static List<string> GetAcctInfo(int empID)
        {
            List<string> userInfo = new List<string>();

            // Lambda function that finds the user using their employee ID
            XElement acct =
                Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Attribute("empID").Value == empID.ToString()).Single();

            // Adds all the specified account info to the returned list
            userInfo.Add(acct.Value);
            userInfo.Add(acct.Attribute("empID").Value);
            userInfo.Add(acct.Attribute("password").Value);
            userInfo.Add(acct.Attribute("type").Value);
            userInfo.Add(acct.Attribute("color").Value);

            return userInfo;
        }

        // Returns a list of all employee ids
        public static List<int> GetAllEmpIDs()
        {
            List<int> empIDs = new List<int>();

            // LINQ query that finds all employees in the xml document
            var employees = from children in Accounts.Element("LocalAccount").Element("Users").Descendants()
                            select children;

            foreach (var employee in employees)
            {
                empIDs.Add(int.Parse(employee.Attribute("empID").Value));
            }

            return empIDs;
        }

        // Deletes a user account
        public static void DeleteUser(string userID)
        {
            if (CheckUser(userID))
            {
                // Lambda function that finds the user using their user ID
                XElement user
                    = Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();

                user.Remove();

                Accounts.Save("accts.xml");
            }
            else
            {
                throw new XmlException();
            }
        }

        // Changes a user password
        public static void ChangePassword(string userID, string password)
        {
            if (CheckUser(userID))
            {
                // Lambda function that finds the user using their user ID
                XElement user =
                    Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();


                user.Attribute("password").Value = password;

                Accounts.Save("accts.xml");
            }
            else
            {
                throw new XmlException();
            }
        }

        // Changes an account's type(privileges)
        public static void ChangeAcctType(string userID, string type)
        {
            if (CheckUser(userID))
            {
                // Lambda function that finds the user using their user ID
                XElement user =
                    Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();

                user.Attribute("type").Value = type;

                Accounts.Save("accts.xml");
            }
            else
            {
                throw new XmlException();
            }
        }

        // Changes the user's prefered form color
        public static void ChangeUserColor(string userID, string color)
        {
            if (CheckUser(userID))
            {
                // Lambda function that finds the user using their user ID
                XElement user =
                    Accounts.Element("LocalAccount").Element("Users").Descendants("User").Where(x => x.Value == userID).Single();

                user.Attribute("color").Value = color;

                Accounts.Save("accts.xml");
            }
            else
            {
                throw new XmlException();
            }
        }

        // Changes the admin code for account creation
        public static void ChangeAdminCode(string newAdminCode)
        {
            XElement oldAdminCode = Accounts.Element("LocalAccount").Element("Code").Element("AdminCode");
            oldAdminCode.Value = newAdminCode;
            Accounts.Save("accts.xml");
        }

        #endregion

        #region [ Helper Methods ]

        // Loads the accts xml document using the given path
        private static void LoadXDoc(string path)
        {
            Accounts = XDocument.Load(path);
        }

        // Creates the accts xml document with using a predefined structure
        private static void CreateXDoc()
        {
            Accounts = new XDocument(new XElement("LocalAccount",
                new XElement("Users"),
                new XElement("Code",
                    new XElement("AdminCode"))));
            Accounts.Declaration = new XDeclaration("1.0", "utf-8", "yes");
            Accounts.Save("accts.xml");
        }

        // Checks to see if the given user account exists
        private static bool CheckUser(string userID)
        {
            var checkUser = from children in Accounts.Element("LocalAccount").Element("Users").Descendants("User")
                            where children.Value == userID
                            select children;

            if (checkUser.Count() == 1)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion
    }
}